#include <iostream>
#include <cmath>
#include "Constants.hpp"
#include "DisplayManager.hpp"

#define FOREACH(it, data) \
    for (__typeof((data).begin()) it = (data).begin(); it != (data).end(); ++it)

DisplayManager::DisplayManager() { // {{{
    paths = alt_buf = NULL;
} // }}}

DisplayManager::~DisplayManager() { // {{{
    destroy_bitmap(alt_buf);
    destroy_bitmap(paths);
    END_OF_MAIN(); // Allegro macro (like a destructor)
} // }}}

bool DisplayManager::init(ObjectManager* objectmanager, unsigned w, unsigned h, int x, int y) { // {{{
    allegro_init();
    install_keyboard();
    // install_mouse();
    // install_timer();

    set_color_depth(24);
    if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, w, h, x, y)) { // if initialization fails
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Graphics initialization failed\n%s\n", allegro_error);
        std::cout << "Graphics initialization failed:" << std::endl << allegro_error << std::endl;
        return false;
    }

    running = true;

    obj_mgr = objectmanager;
    displayScale = DisplayManager__DEFAULT_SCALE;
    alt_buf = create_bitmap(w, h);
    paths = create_bitmap(w, h);

    clear_to_color(screen, makecol(0, 0, 0));
    clear_to_color(paths, makecol(0, 0, 0));

    return true;
} // }}}

bool DisplayManager::isRunning() { // {{{
    return running && screen != NULL;
} // }}}

void DisplayManager::drawObjects() { // {{{
    checkKeys();

    ObjContainer* obj_cont = obj_mgr->getAllObjects();

    clear_to_color(alt_buf, makecol(0, 0, 0));
    blit(paths, alt_buf, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

    FOREACH(obj, *(obj_cont)) {
        int r = 1 + int(DisplayManager__OBJECT_SIZE * log(obj->getM())-10); // +1 because we want the things to be visible
        int x = int(obj->getX() * displayScale + SCREEN_W/2);
        int y = int(obj->getY() * displayScale + SCREEN_H/2);

        if (obj->isStar())
            circlefill(alt_buf, x, y, r, makecol(255,255,0));
        else
            circlefill(alt_buf, x, y, r, makecol(255,255,255));
        textout_ex(alt_buf, font, obj->getLabel(), x+5, y+5, makecol(100,155,255), -1);

        putpixel(paths, int(obj->getX() * displayScale + SCREEN_W/2), int(obj->getY() * displayScale + SCREEN_H/2), makecol(0, 155, 0));
    }

    // additional stuff (scale, time...) {{{
    // time estimate
    textprintf_ex(alt_buf, font, 20, SCREEN_H-28, makecol(255, 0, 0), -1, "Elapsed: %.2f years", obj_mgr->getTimeYears());
    // scale (bottom right)
    if (displayScale > 8e-11) {
        line(alt_buf, SCREEN_W-20, SCREEN_H-20, SCREEN_W-20-(AU * displayScale), SCREEN_H-20, makecol(255, 0, 0));
        textout_right_ex(alt_buf, font, "1AU", SCREEN_W-19, SCREEN_H-30, makecol(255, 0, 0), -1);
    }
    else {
        line(alt_buf, SCREEN_W-20, SCREEN_H-20, SCREEN_W-20-(LY * displayScale), SCREEN_H-20, makecol(255, 0, 0));
        textout_right_ex(alt_buf, font, "1LY", SCREEN_W-19, SCREEN_H-30, makecol(255, 0, 0), -1);
    }
    // }}}

    blit(alt_buf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
} // }}}

void DisplayManager::checkKeys() { // {{{
    if (key[KEY_Q] || key[KEY_ESC])
        running = false;

    if (key[KEY_P]) // pause till next keypress
        readkey();

    if (key[KEY_I]) // zoom in
        zoomIn();
    if (key[KEY_O]) // zoom out
        zoomOut();

    if (key[KEY_F]) // decrease precision / increase speed
        obj_mgr->changePrecisionBy(DT_STEP_SIZE);
    if (key[KEY_S]) // increase precision / decrease speed
        obj_mgr->changePrecisionBy(1/DT_STEP_SIZE);
} // }}}

void DisplayManager::zoomIn() { // {{{
    displayScale *= DisplayManager__ZOOM_STEP_SIZE;
    clear_to_color(paths, makecol(0, 0, 0));
} // }}}
void DisplayManager::zoomOut() { // {{{
    displayScale /= DisplayManager__ZOOM_STEP_SIZE;
    clear_to_color(paths, makecol(0, 0, 0));
} // }}}
